/*
 * $Id:
 *
 * Copyright (C) 2009 Refiner Inc.
 * All right reserved
 *
 *============================================================
 * +rVPN : utility
 *============================================================
 * abstract: This module provides utility functions
 * author  : SUG
 * history :
 *   1.0.0 090531 First Release
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/types.h>

#include "rrutil.h"

/*
 * 標準メモリ制御関数のラッパー
 */

/**
 * 動的なメモリの割り当て
 *
 * @param size メモリサイズ
 *
 * @return NULL 失敗
 * @return メモリ ポインタ
 */
void *
rr_malloc(u_long size)
{
    if (size == 0)
    {
        return NULL;
    }
#ifdef RR_TRACE
    {
        void *ptr = malloc(size);
        if (current_trace_flag)
            printf("rr_malloc: %p\n", ptr);
        _TRC_MALLOC(ptr);
        return ptr;
    }
#else
    return malloc(size);
#endif
}

/**
 * 動的なメモリの割り当て
 *
 * @param nmemb 配列数
 * @param size 配列メモリサイズ
 *
 * @return NULL 失敗
 * @return メモリ ポインタ
 */
void *
rr_calloc(u_long nmemb, u_long size)
{
    if ((nmemb == 0) || (size == 0))
    {
        return NULL;
    }
#ifdef RR_TRACE
    {
        void *ptr = calloc(nmemb, size);
        if (current_trace_flag)
            printf("rr_calloc: %p\n", ptr);
        _TRC_MALLOC(ptr);
        return ptr;
    }
#else
    return calloc(nmemb, size);
#endif
}

/**
 * 割り当てられたメモリの開放
 *
 * @param ptr メモリ ポインタ
 *
 * @return なし
 */
void
rr_free(void *ptr)
{
    if (ptr != NULL)
    {
#ifdef RR_TRACE
        if (current_trace_flag)
            printf("rr_free: %p\n", ptr);
        _TRC_FREE(ptr);
#endif
        free(ptr);
    }
}

#if USE_THREAD

#include <pthread.h>

/**
 * MUTEX の初期化
 *
 * @param なし
 *
 * @return pthread_mutex_t 構造体ポインタ (void *)
 */
void *
rr_mutex_init(void)
{
    pthread_mutex_t *mutex;
#ifdef notdef
    pthread_mutexattr_t mutex_attr;     /* 2008.12.01 E.MURATA */
#endif

    mutex = (pthread_mutex_t *)rr_malloc(sizeof(pthread_mutex_t));
    if (mutex == NULL)
    {
        /*rsip_log(_LOG_FATAL, "FATAL:rr_mutex_init: rr_malloc() failed!");*/
        return NULL;
    }
        /* MRT ... Error Check! */
#ifdef notdef
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK_NP);
    pthread_mutex_init(&mutex, &mutex_attr);
#else
    pthread_mutex_init(mutex, NULL);
#endif
    return (void *)mutex;
}

/**
 * MUTEX のロック
 *
 * @param mutex pthread_mutex_t 構造体ポインタ (void *)
 *
 * @return 0 正常
 * @return !0 エラー
 */
int
rr_mutex_lock(void *mutex)
{
    return pthread_mutex_lock((pthread_mutex_t *)mutex);
}

/**
 * MUTEX のアンロック
 *
 * @param mutex pthread_mutex_t 構造体ポインタ (void *)
 *
 * @return 0 正常
 * @return !0 エラー
 */
int
rr_mutex_unlock(void *mutex)
{
    return pthread_mutex_unlock((pthread_mutex_t *)mutex);
}

/**
 * MUTEX の開放
 *
 * @param mutex pthread_mutex_t 構造体ポインタ (void *)
 *
 * @return 0 正常
 * @return !0 エラー
 */
int
rr_mutex_destroy(void *mutex)
{
    int sts;

    if (mutex == NULL)
    {
        return 0;
    }
    sts = pthread_mutex_destroy((pthread_mutex_t *)mutex);
    rr_free(mutex);
    return sts;
}

#endif /* USE_THREAD */

/**
 * キューの初期化
 *
 * @param que rr_que_t 構造体ポインタ
 *
 * @return なし
 */
void
rr_que_init(rr_que_t *que)
{
    que->prev = NULL;
    que->next = NULL;
    que->list = NULL;
}

/**
 * キューリストの初期化
 *
 * @param list rr_que_list_t 構造体ポインタ
 *
 * @return なし
 */
void
rr_que_list_init(rr_que_list_t *list)
{
    list->prev = (rr_que_t *)list;
    list->next = (rr_que_t *)list;
    list->qlen = 0;
}

/**
 * キューの先頭に接続する
 *
 * @param list rr_que_list_t 構造体ポインタ
 * @param new rr_que_t 構造体ポインタ
 *
 * @return なし
 */
void
_rr_enque_head(rr_que_list_t *list, rr_que_t *new)
{
    rr_que_t *prev, *next;

    new->list = list;
    list->qlen++;
    next = list->next;
    prev = next->prev;
    new->next = next;
    new->prev = prev;
    next->prev = new;
    prev->next = new;
}

void
rr_enque_head(rr_que_list_t *list, rr_que_t *new)
{
    /* lock */
    _rr_enque_head(list, new);
    /* unlock */
}

/**
 * キューの最後に接続する
 *
 * @param list rr_que_list_t 構造体ポインタ
 * @param new rr_que_t 構造体ポインタ
 *
 * @return なし
 */
void
rr_enque_tail(rr_que_list_t *list, rr_que_t *new)
{
    rr_que_t *prev, *next;

    new->list = list;
    list->qlen++;
    next = (rr_que_t *)list;
    prev = next->prev;
    new->next = next;
    new->prev = prev;
    next->prev = new;
    prev->next = new;
}

/**
 * 指定位置の直前に接続する
 *
 * @paran pos 指定位置(rr_que_t 構造体ポインタ)
 * @param new rr_que_t 構造体ポインタ
 *
 * @return なし
 */
void
rr_enque_before(rr_que_t *pos, rr_que_t *new)
{
    new->list = pos->list;
    pos->list->qlen++;
    new->next = pos;
    new->prev = pos->prev;
    pos->prev = new;
    new->prev->next = new;
}

/**
 * 指定位置の直後に接続する
 *
 * @paran pos 指定位置(rr_que_t 構造体ポインタ)
 * @param new rr_que_t 構造体ポインタ
 *
 * @return なし
 */
void
rr_enque_after(rr_que_t *pos, rr_que_t *new)
{
    new->list = pos->list;
    pos->list->qlen++;
    new->next = pos->next;
    new->prev = pos;
    pos->next = new;
    new->next->prev = new;
}

/**
 * キューの先頭から取り出す
 *
 * @param list rr_que_list_t 構造体ポインタ
 *
 * @return rr_que_t 構造体ポインタ
 */
rr_que_t *
rr_deque_head(rr_que_list_t *list)
{
    rr_que_t *next, *prev, *result;

    if (list->qlen == 0)
    {
        return NULL;
    }
    result = list->next;
    prev = result->prev;
    next = result->next;
    list->qlen--;
    next->prev = prev;
    prev->next = next;
    result->next = NULL;
    result->prev = NULL;
    result->list = NULL;
    return result;
}

/**
 * キューの最後から取り出す
 *
 * @param list rr_que_list_t 構造体ポインタ
 *
 * @return rr_que_t 構造体ポインタ
 */
rr_que_t *
rr_deque_tail(rr_que_list_t *list)
{
    rr_que_t *next, *prev, *result;

    if (list->qlen == 0)
    {
        return NULL;
    }
    result = list->prev;
    prev = result->prev;
    next = result->next;
    list->qlen--;
    next->prev = prev;
    prev->next = next;
    result->next = NULL;
    result->prev = NULL;
    result->list = NULL;
    return result;
}

/**
 * オフセットを指定してキューから取り出す
 *
 * @param list rr_que_list_t 構造体ポインタ
 * @param off 先頭からのオフセット
 *
 * @return rr_que_t 構造体ポインタ
 */
rr_que_t *
rr_deque_offset(rr_que_list_t *list, int off)
{
    rr_que_t *next, *prev, *result;

    if (off < 0 || off >= list->qlen)
    {
        return NULL;
    }
    for (result = list->next ; off ; off--)
    {
        result = result->next;
    }
    prev = result->prev;
    next = result->next;
    list->qlen--;
    next->prev = prev;
    prev->next = next;
    result->next = NULL;
    result->prev = NULL;
    result->list = NULL;
    return result;
}

/**
 * オフセットを指定してキューを参照する
 *
 * @param list rr_que_list_t 構造体ポインタ
 * @param off 先頭からのオフセット
 *
 * @return rr_que_t 構造体ポインタ
 */
rr_que_t *
rr_que_get_offset(rr_que_list_t *list, int off)
{
    rr_que_t *result;

    if (off < 0 || off >= list->qlen)
    {
        return NULL;
    }
    for (result = list->next ; off ; off--)
    {
        result = result->next;
    }
    return result;
}

/**
 * 指定のキューをリストから取り出す
 *
 * @param que rr_que_t 構造体ポインタ
 *
 * @return 0 正常
 * @return -1 エラー
 */
int
rr_deque_que(rr_que_t *que)
{
    rr_que_list_t *list;
    rr_que_t *next, *prev;

    list = que->list;
    if (list == NULL || list->qlen == 0)
    {
        return -1; // ???
    }
    prev = que->prev;
    next = que->next;
    list->qlen--;
    next->prev = prev;
    prev->next = next;
    que->next = NULL;
    que->prev = NULL;
    que->list = NULL;
    return 0;
}

/**
 * 乱数種の設定
 *
 * @param seed 種値
 *
 * @return なし
 */
static u_int Seed = 1;
void
rr_srand(u_int seed)
{
    Seed = seed;
}

#define CONSTANT_A      1103515245
#define CONSTANT_B      1234567
#define CONSTANT_C      0x80000000

/**
 * 乱数の生成
 *
 * @param なし
 *
 * @return 乱数値
 */
int
rr_rand(void)
{
    Seed = (((Seed * CONSTANT_A) + CONSTANT_B) % CONSTANT_C);
    return (int)Seed;
}

/*
 * 標準ライブラリ関数ラッパー
 */

/**
 * atoi 関数
 *
 * @param nptr 文字列ポインタ
 *
 * @return 変換数値
 */
int
rr_atoi(char *nptr)
{
    return rr_natoi(nptr, rr_strlen(nptr));
}

/**
 * データ長を指定した atoi 関数
 *
 * @param nptr 文字列ポインタ
 * @param len 文字バイト数
 *
 * @return 変換数値
 */
int
rr_natoi(char *nptr, int len)
{
    int value;
    char n;

    for (value = 0 ; len ; len--)
    {
        n = *nptr++;
        if ((n < '0') || (n > '9'))
        {
            return -1;
        }
        n -= '0';
        value = value * 10 + (int)n;
    }
    return value;
}

/**
 * バイナリ数をストリングに変換した場合のストリング長の計算
 *
 * @param n バイナリ数
 *
 * @return 文字数
 *
 * 正の値のみ
 */
int
rr_itoa_len(int n)
{
    int len;

    if (n <= 0)
    {
        return 1;
    }
    for (len = 0 ; n ; len++)
    {
        n /= 10;
    }
    return len;
}

/**
 * バイナリ数をストリングに変換しバッファに格納
 *
 * @param n バイナリ数
 * @param cp 文字格納バッファポインタ
 *
 * @return 次のバッファポインタ
 *
 * 正の値のみ
 */
char *
rr_itoa(int n, char *cp)
{
    int i, len;
    char ch;

    if (n <= 0)
    {
        *cp++ = '0';
        return cp;
    }
    len = rr_itoa_len(n);
    for (i = len ; n ; )
    {
        ch = '0' + (n % 10);
        cp[--i] = ch;
        n /= 10;
    }
    return cp + len;
}

/*
 * 標準ストリング関数のラッパー
 */

/* memset */
void *
rr_memset(void *s, int c, int n)
{
    return memset(s, c, (size_t)n);
}

/* memcpy */
void *
rr_memcpy(void *dest, void *src, int n)
{
    return memcpy(dest, src, (size_t)n);
}

/* strcpy */
char *
rr_strcpy(char *dest, char *src)
{
    return strcpy(dest, src);
}

/* strncpy */
char *
rr_strncpy(char *dest, char *src, int n)
{
    return strncpy(dest, src, (size_t)n);
}

/* strchr */
char *
rr_strchr(char *s, int c)
{
    return strchr(s, c);
}

/* strchr */
char *
rr_strrchr(char *s, int c)
{
    return strchr(s, c);
}

/* strlen */
int
rr_strlen(char *s)
{
    if (s == NULL)
    {
        return 0;
    }
    return strlen(s);
}

/* strcmp */
int
rr_strcmp(char *s1, char *s2)
{
    return strcmp(s1, s2);
}

/* strncmp */
int
rr_strncmp(char *s1, char *s2, int n)
{
    if (n == 0)
    {
        return 0;
    }
    return strncmp(s1, s2, (size_t)n);
}

/* strcasecmp */
int
rr_strcasecmp(char *s1, char *s2)
{
    return strcasecmp(s1, s2);
}

/* strncasecmp */
int
rr_strncasecmp(char *s1, char *s2, int n)
{
    if (n == 0)
    {
        return 0;
    }
    return strncasecmp(s1, s2, (size_t)n);
}

/*
 * ストリング制御関数
 */

/**
 * ストリング構造体の初期化
 *
 * @param str rr_str_t 構造体ポインタ
 *
 * @return なし
 */

void
rr_str_init(rr_str_t *str)
{
    str->ptr = NULL;
    str->len = 0;
    str->dynamic = 0;
}

/**
 * ストリング構造体内メモリのアロケート
 *
 * @param str rr_str_t 構造体ポインタ
 *
 * @return アロケートされたメモリポインタ
 */
char *
rr_str_alloc(rr_str_t *str, int len)
{
    rr_str_free(str);
    str->ptr = (char*)rr_malloc(len);
    if (str->ptr == NULL)
    {
        /*rsip_log(_LOG_FATAL, "FATAL:rr_str_alloc: rr_malloc() failed!");*/
        return NULL;
    }
    str->len = len;
    str->dynamic = 1;
    return str->ptr;
}

/**
 * ストリング構造体内メモリの開放
 *
 * @param str rr_str_t 構造体ポインタ
 *
 * @return なし
 *
 * 構造体そのものは開放しない
 */
void
rr_str_free(rr_str_t *str)
{
    if ((str->ptr != NULL) && str->dynamic)
    {
        rr_free(str->ptr);
    }
    rr_str_init(str);
}

/**
 * 指定のバッファにストリング構造体のストリングをコピー
 *
 * @param str rr_str_t 構造体ポインタ
 * @param dest コピー先バッファポインタ
 *
 * @return バッファの次の書き込みポインタ
 */
char *
rr_str_memcpy(rr_str_t *str, char *dest)
{
    if (str->len == 0)
    {
        return dest;
    }
    rr_memcpy(dest, str->ptr, str->len);
    return dest + str->len;
}

/**
 * ストリング構造体内にメモリをアロケートし、ストリングをコピー １
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 *
 * @return アロケートされたメモリポインタ
 */
char *
rr_str_alcpy(rr_str_t *str, char *ptr)
{
    char *cp;
    int len;

    len = rr_strlen(ptr);
    cp = rr_str_alloc(str, len + 1);
    if (cp != NULL)
    {
        rr_strcpy(cp, ptr);
        str->len--;
    }
    return cp;
}

/**
 * ストリング構造体内にメモリをアロケートし、ストリングをコピー ２
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 * @param len ストリング長
 *
 * @return アロケートされたメモリポインタ
 *
 * len + 1 でアロケートし、NULL ターミネートとする
 * ただし、データ長は NULL を含まず
 */
char *
rr_str_alncpy(rr_str_t *str, char *ptr, int len)
{
    char *cp;

    cp = rr_str_alloc(str, len + 1);
    if (cp != NULL)
    {
        rr_memcpy(cp, ptr, len);
        cp[len] = '\0';
        str->len--;
    }
    return cp;
}

/**
 * ストリング構造体内にメモリをアロケートし、ストリングをコピー
 *
 * @param to コピー先 rr_str_t 構造体ポインタ
 * @param from コピー元 rr_str_t 構造体ポインタ
 *
 * @return 0 正常
 * @return -1 エラー
 */
int
rr_str_clone(rr_str_t *to, rr_str_t *from)
{
    if (from->ptr == NULL)
    {
        return 0;
    }
    if (rr_str_alncpy(to, from->ptr, from->len) == NULL)
    {
        return -1;
    }
    return 0;
}

/**
 * 構造体の比較 １
 *
 * @param str1 rr_str_t 構造体ポインタ
 * @param str2 rr_str_t 構造体ポインタ
 *
 * @return 0 一致
 * @return 1 不一致
 */
int
rr_str_scmp(rr_str_t *str1, rr_str_t *str2)
{
    if (str1->len == str2->len)
    {
        return rr_strncmp(str1->ptr, str2->ptr, str1->len);
    }
    return 1;
}

/**
 * 構造体の比較 ２
 *
 * @param str1 rr_str_t 構造体ポインタ
 * @param str2 rr_str_t 構造体ポインタ
 *
 * @return 0 一致
 * @return 1 不一致
 *
 * 大文字、小文字を区別しない
 */
int
rr_str_scasecmp(rr_str_t *str1, rr_str_t *str2)
{
    if (str1->len == str2->len)
    {
        return rr_strncasecmp(str1->ptr, str2->ptr, str1->len);
    }
    return 1;
}

/**
 * ストリングの比較 １
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 *
 * @return 0 一致
 * @return 1 不一致
 */
int
rr_str_cmp(rr_str_t *str, char *ptr)
{
    if (str->len == rr_strlen(ptr))
    {
        return rr_strncmp(str->ptr, ptr, str->len);
    }
    return 1;
}

/**
 * ストリングの比較 ２
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 * @param len ストリング長
 *
 * @return 0 一致
 * @return 1 不一致
 */
int
rr_str_ncmp(rr_str_t *str, char *ptr, int len)
{
    if (str->len == len)
    {
        return rr_strncmp(str->ptr, ptr, len);
    }
    return 1;
}

/**
 * ストリングの比較 ３
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 *
 * @return 0 一致
 * @return 1 不一致
 *
 * 大文字、小文字を区別しない
 */
int
rr_str_casecmp(rr_str_t *str, char *ptr)
{
    if (str->len == rr_strlen(ptr))
    {
        return rr_strncasecmp(str->ptr, ptr, str->len);
    }
    return 1;
}

/**
 * ストリングの比較 ４
 *
 * @param str rr_str_t 構造体ポインタ
 * @param ptr ストリングポインタ
 * @param len ストリング長
 *
 * @return 0 一致
 * @return 1 不一致
 *
 * 大文字、小文字を区別しない
 */
int
rr_str_ncasecmp(rr_str_t *str, char *ptr, int len)
{
    if (str->len == len)
    {
        return rr_strncasecmp(str->ptr, ptr, len);
    }
    return 1;
}

/**
 * 時刻の取得
 *
 * @param tv rr_timeval_t ポインタ
 *
 * @return 0 成功
 * @return -1 失敗
 */
int
rr_gettimeofday(rr_timeval_t *tv)
{
    int i;

    i = gettimeofday((struct timeval *)tv, NULL);
    if (i < 0)
    {
        /*rsip_log(_LOG_FATAL, "FATAL:rr_gettimeofday: gettimeofday() failed!");*/
    }
    return i;
}
